﻿<#
.SYNOPSIS
    一键安装、加固、自适应端口的 Windows 11 OpenSSH Server
.AUTHOR
    (Your Name)
#>
#Requires -RunAsAdministrator
$ErrorActionPreference = 'Stop'

#----------------------------------------------------------
# 可调参数
#----------------------------------------------------------
$PreferredPort = 22          # 首选端口
$AutoPort      = $true       # 若被占用则自动换端口
$AutoUser      = $true       # 把当前用户加入 Users 组
$AutoKey       = $true       # 若同目录存在 id_rsa.pub 则自动追加

#----------------------------------------------------------
# 工具函数
#----------------------------------------------------------
function Show-Header([string]$Text) {
    Write-Host "`n==> $Text" -ForegroundColor Cyan
}
function Test-PortInUse([int]$Port) {
    return [bool](Get-NetTCPConnection -LocalPort $Port -ErrorAction SilentlyContinue)
}

#----------------------------------------------------------
# 1. 安装
#----------------------------------------------------------
Show-Header "安装 OpenSSH Server"
$cap = Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
if ($cap.Online -eq $false) { throw "安装失败" }

#----------------------------------------------------------
# 2. 自适应端口
#----------------------------------------------------------
$Port = $PreferredPort
if ($AutoPort -and (Test-PortInUse $Port)) {
    $Port = 2222
    while (Test-PortInUse $Port) { $Port++ }
    Write-Host "端口 $PreferredPort 被占用，已自动改为 $Port" -ForegroundColor Yellow
}

#----------------------------------------------------------
# 3. 服务
#----------------------------------------------------------
Show-Header "启动并设为自动"
Start-Service sshd
Set-Service   sshd -StartupType Automatic

#----------------------------------------------------------
# 4. 防火墙
#----------------------------------------------------------
Show-Header "配置防火墙"
$ruleName = "OpenSSH-Server-In-TCP-$Port"
if (-not (Get-NetFirewallRule -Name $ruleName -ErrorAction SilentlyContinue)) {
    New-NetFirewallRule `
        -Name $ruleName `
        -DisplayName "OpenSSH Server ($Port)" `
        -Enabled True `
        -Direction Inbound `
        -Protocol TCP `
        -Action Allow `
        -LocalPort $Port `
        -Profile Any | Out-Null
}

#----------------------------------------------------------
# 5. sshd_config
#----------------------------------------------------------
Show-Header "配置 sshd_config"
$conf = "$env:ProgramData\ssh\sshd_config"
$lines = Get-Content $conf
$lines = $lines `
    -replace '(?i)^#\s*Port\s+\d+',            "Port $Port" `
    -replace '(?i)^Port\s+\d+',                "Port $Port" `
    -replace '(?i)^#\s*PubkeyAuthentication\s+\w+', 'PubkeyAuthentication yes' `
    -replace '(?i)^PubkeyAuthentication\s+\w+',      'PubkeyAuthentication yes' `
    -replace '(?i)^#\s*PasswordAuthentication\s+\w+', 'PasswordAuthentication yes' `
    -replace '(?i)^PasswordAuthentication\s+\w+',      'PasswordAuthentication yes' `
    -replace '(?i)^#\s*PermitEmptyPasswords\s+\w+',    'PermitEmptyPasswords yes' `
    -replace '(?i)^PermitEmptyPasswords\s+\w+',        'PermitEmptyPasswords yes'
$lines | Set-Content $conf -Encoding UTF8 -Force
Restart-Service sshd

#----------------------------------------------------------
# 6. 用户组
#----------------------------------------------------------
if ($AutoUser) {
    $user = (whoami).Split('\')[-1]
    Show-Header "将 $user 加入 Users 组"
    Add-LocalGroupMember -Group "Users" -Member $user -ErrorAction SilentlyContinue
}

#----------------------------------------------------------
# 7. 自动追加公钥
#----------------------------------------------------------
$sshDir  = "$env:USERPROFILE\.ssh"
$keyFile = "$PSScriptRoot\id_rsa.pub"
if ($AutoKey -and (Test-Path $keyFile)) {
    Show-Header "发现公钥，自动追加"
    if (-not (Test-Path $sshDir)) { New-Item -ItemType Directory -Path $sshDir -Force | Out-Null }
    $auth = "$sshDir\authorized_keys"
    Get-Content $keyFile | Add-Content $auth
    icacls $auth /inheritance:r /grant:r "$env:USERNAME:(R)" | Out-Null
}

#----------------------------------------------------------
# 8. 生成一键连接脚本
#----------------------------------------------------------
$ip = (Get-NetIPAddress -AddressFamily IPv4 -InterfaceAlias (Get-NetConnectionProfile | Select-Object -First 1).InterfaceAlias).IPAddress
$user = (whoami).Split('\')[-1]

# Windows 批处理
@"
@echo off
echo Connecting to ${user}@${ip}:${Port} ...
ssh -p ${Port} ${user}@${ip}
pause
"@ | Out-File -FilePath "$PSScriptRoot\connect.cmd" -Encoding ascii

# Linux/macOS Shell
@"
#!/bin/bash
echo Connecting to ${user}@${ip}:${Port} ...
ssh -p ${Port} ${user}@${ip}
"@ | Out-File -FilePath "$PSScriptRoot\connect.sh"  -Encoding ascii

#----------------------------------------------------------
# 9. 诊断摘要
#----------------------------------------------------------
Show-Header "诊断摘要"
Write-Host ("服务状态: {0}" -f (Get-Service sshd).Status) -ForegroundColor Green
Write-Host "监听端口  : $Port" -ForegroundColor Green
Write-Host "防火墙规则: $($ruleName)" -ForegroundColor Green
Write-Host "一键脚本  : connect.cmd (Windows) / connect.sh (Linux/macOS)" -ForegroundColor Green
Write-Host ""
Write-Host "示例连接：" -ForegroundColor Cyan
Write-Host "  ssh -p $Port $user@$ip" -ForegroundColor White
